package com.runjian.rundo.iot.agent.iec104.session;

import java.util.concurrent.Future;
import java.util.concurrent.atomic.AtomicInteger;

import com.runjian.rundo.iot.agent.common.network.tcp.client.TcpNetworkClient;
import com.runjian.rundo.iot.agent.common.network.tcp.client.session.TcpClientDeviceSession;
import com.runjian.rundo.iot.agent.iec104.consts.FrameType;
import com.runjian.rundo.iot.agent.iec104.consts.Iec104Constant;
import com.runjian.rundo.iot.agent.iec104.parser.frame.AIec104Frame;
import com.runjian.rundo.iot.agent.iec104.parser.frame.IIec104Frame;
import com.runjian.rundo.iot.agent.iec104.parser.frame.SIec104Frame;
import com.runjian.rundo.iot.agent.iec104.parser.frame.UIec104Frame;

import cn.hutool.core.util.ByteUtil;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.Channel;
import lombok.Getter;
import lombok.Setter;
import lombok.extern.slf4j.Slf4j;

/**
 *
 * 104协议的设备session
 * 
 * @author CaoQingHua
 */
@Slf4j
@Getter
@Setter
public class Iec104TcpClientDeviceSession extends TcpClientDeviceSession {

    /**
     * 发送号
     */
    private AtomicInteger sendNo = new AtomicInteger(-1);

    /**
     * 接收号(对方的发送号)
     */
    private AtomicInteger receiveNo = new AtomicInteger(0);

    /**
     * ASK号(即收到对方S侦号或接收号)
     */
    private AtomicInteger ackNo = new AtomicInteger(0);

    public Iec104TcpClientDeviceSession(TcpNetworkClient tcpNetworkClient, Channel channel) {
        super(tcpNetworkClient, channel);

    }

    /**
     * 写入I侦数据
     *
     * @param iec104Frame 侦对象
     * @return Future对象,可通过此对象来检验是否已执行
     */
    public synchronized Future<Void> writeIAndFlush(IIec104Frame iec104Frame) {
        // 写入发送号加1
        this.sendNo.getAndIncrement();
        // 写入相关序号值
        iec104Frame.setSendNo((short)this.sendNo.get());
        // 写入相关序号值
        iec104Frame.setReceiveNo((short)this.receiveNo.get());
        if (log.isDebugEnabled()) {
            log.debug("发送I侦:{}", iec104Frame.console());
        }
        return this.writeIAndFlush(iec104Frame.assemblyByteBuf());
    }

    /**
     * 写入I侦数据
     * 
     * @param byteBuf 缓存对象
     * @return Future对象,可通过此对象来检验是否已执行
     */
    private Future<Void> writeIAndFlush(ByteBuf byteBuf) {
        // 拼装发送号
        ByteBuf sendByteBuf = Unpooled.buffer();
        // 写入104头
        sendByteBuf.writeByte(Iec104Constant.HEADER);
        int contentLength = byteBuf.readableBytes();
        // 下面发送号及接收号4位字节
        sendByteBuf.writeByte(ByteUtil.intToByte(contentLength + 4));
        // 发送号2位字节
        sendByteBuf.writeBytes(ByteUtil.shortToBytes((short)(this.getSendNo().get() << 1 & 0xFFFF)));
        // 接收号2位字节
        sendByteBuf.writeBytes(ByteUtil.shortToBytes((short)(this.getReceiveNo().get() << 1 & 0xFFFF)));
        // 104协议在输出时，要增加68头信息,及相关的发送号
        sendByteBuf.writeBytes(byteBuf);
        // 释放
        byteBuf.release();
        return this.writeAndFlush(sendByteBuf);
    }

    /**
     * 写入S侦数据
     *
     * @param sIec104Frame s侦对象
     * @return Future对象,可通过此对象来检验是否已执行
     */
    public Future<Void> writeSAndFlush(SIec104Frame sIec104Frame) {
        // 发送的S侦
        sIec104Frame.setReceiveNo((short)this.getReceiveNo().get());
        if (log.isDebugEnabled()) {
            log.debug("发送S侦:{}", sIec104Frame.console());
        }
        return this.writeSAndFlush(sIec104Frame.assemblyByteBuf());
    }

    /**
     * 写入S侦数据
     *
     * @param byteBuf 缓存对象
     * @return Future对象,可通过此对象来检验是否已执行
     */
    private Future<Void> writeSAndFlush(ByteBuf byteBuf) {
        // 拼装发送号
        ByteBuf sendByteBuf = Unpooled.buffer();
        // 写入104头
        sendByteBuf.writeByte(Iec104Constant.HEADER);
        // 写入104头
        sendByteBuf.writeByte(ByteUtil.intToByte(4));
        // 默认为1
        sendByteBuf.writeByte(ByteUtil.intToByte(1));
        // 默认为0
        sendByteBuf.writeByte(ByteUtil.intToByte(0));
        // 接收号2位字节
        sendByteBuf.writeBytes(ByteUtil.shortToBytes((short)(this.getReceiveNo().get() << 1 & 0xFFFF)));
        // 释放
        byteBuf.release();
        return this.writeAndFlush(sendByteBuf);
    }

    /**
     * 写入U侦数据
     *
     * @param uIec104Frame 缓存对象
     * @return Future对象,可通过此对象来检验是否已执行
     */
    public Future<Void> writeUAndFlush(UIec104Frame uIec104Frame) {
        if (log.isDebugEnabled()) {
            log.debug("发送U侦:{}", uIec104Frame.console());
        }
        // 写入并刷新数据
        return this.writeUAndFlush(uIec104Frame.assemblyByteBuf());
    }

    /**
     * 写入U侦数据
     *
     * @param byteBuf 缓存对象
     * @return Future对象,可通过此对象来检验是否已执行
     */
    private Future<Void> writeUAndFlush(ByteBuf byteBuf) {
        // 拼装发送号
        ByteBuf sendByteBuf = Unpooled.buffer();
        // 写入104头
        sendByteBuf.writeByte(Iec104Constant.HEADER);
        // 写入内容长度
        int contentLength = byteBuf.readableBytes();
        sendByteBuf.writeByte(ByteUtil.intToByte(contentLength));
        // 写入U侦内容
        sendByteBuf.writeBytes(byteBuf);
        // 释放
        byteBuf.release();
        // 写入并刷新数据
        return this.writeAndFlush(sendByteBuf);
    }

    public Future<Void> writeAndFlush(FrameType frameType, ByteBuf byteBuf) {
        if (frameType == FrameType.I) {
            return writeIAndFlush(byteBuf);
        } else if (frameType == FrameType.S) {
            return writeSAndFlush(byteBuf);
        } else if (frameType == FrameType.U) {
            return writeUAndFlush(byteBuf);
        }
        return null;
    }

    public void handleAIec104Frame(AIec104Frame frame) {
        if (frame instanceof IIec104Frame) {
            // 我们的接收号等于对方的发送号
            // 接收号加1
            this.receiveNo.getAndIncrement();
        } else if (frame instanceof SIec104Frame) {
            // 我们发送了N侦,对方才收至的接收号，通过此号处理数据丢失逻辑
            this.ackNo.set(((SIec104Frame)frame).getReceiveNo());
        }
    }
}
